// Copyright (c) 2024 CHANGLEI. All rights reserved.

import 'package:charts_grasp/src/chart/chart_behavior/selection_behavior.dart';
import 'package:charts_grasp/src/chart/chart_segment/chart_segment.dart';
import 'package:charts_grasp/src/chart/chart_series/series.dart';
import 'package:charts_grasp/src/chart/chart_series/series_renderer_properties.dart';
import 'package:charts_grasp/src/chart/common/cartesian_state_properties.dart';
import 'package:charts_grasp/src/chart/user_interaction/selection_renderer.dart';
import 'package:charts_grasp/src/chart/utils/helper.dart';
import 'package:charts_grasp/src/circular_chart/renderer/common.dart';
import 'package:charts_grasp/src/core/core.dart';
import 'package:flutter/material.dart';

class SelectionBehavior {
  SelectionBehavior(
      {bool? enable,
      this.selectedColor,
      this.selectedBorderColor,
      this.selectedBorderWidth,
      this.unselectedColor,
      this.unselectedBorderColor,
      this.unselectedBorderWidth,
      double? selectedOpacity,
      double? unselectedOpacity,
      this.selectionController,
      bool? toggleSelection,})
      : enable = enable ?? false,
        selectedOpacity = selectedOpacity ?? 1.0,
        unselectedOpacity = unselectedOpacity ?? 0.5,
        toggleSelection = toggleSelection ?? true;

  final bool enable;

  final Color? selectedColor;

  final Color? selectedBorderColor;

  final double? selectedBorderWidth;

  final Color? unselectedColor;

  final Color? unselectedBorderColor;

  final double? unselectedBorderWidth;

  final double selectedOpacity;

  final double unselectedOpacity;

  final RangeController? selectionController;

  final bool toggleSelection;

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (other.runtimeType != runtimeType) {
      return false;
    }

    return other is SelectionBehavior &&
        other.enable == enable &&
        other.selectedColor == selectedColor &&
        other.selectedBorderColor == selectedBorderColor &&
        other.selectedBorderWidth == selectedBorderWidth &&
        other.unselectedColor == unselectedColor &&
        other.unselectedBorderColor == unselectedBorderColor &&
        other.unselectedBorderWidth == unselectedBorderWidth &&
        other.selectedOpacity == selectedOpacity &&
        other.unselectedOpacity == unselectedOpacity &&
        other.selectionController == selectionController;
  }

  @override
  int get hashCode {
    final List<Object?> values = <Object?>[
      enable,
      selectedColor,
      selectedBorderColor,
      selectedBorderWidth,
      unselectedColor,
      unselectedBorderColor,
      unselectedBorderWidth,
      selectedOpacity,
      unselectedOpacity,
      selectionController,
    ];
    return Object.hashAll(values);
  }

  SelectionBehaviorRenderer? _selectionBehaviorRenderer;

  void selectDataPoints(int pointIndex, [int seriesIndex = 0]) {
    if (_selectionBehaviorRenderer != null) {
      final dynamic seriesRenderer =
          _selectionBehaviorRenderer!._selectionDetails.stateProperties.chartSeries.visibleSeriesRenderers[seriesIndex];
      assert(
          seriesRenderer is CartesianSeriesRenderer == false ||
              getVisibleDataPointIndex(pointIndex, SeriesHelper.getSeriesRendererDetails(seriesRenderer)) != null,
          'Provided point index is not in the visible range. Provide point index which is in the visible range.',);
      _selectionBehaviorRenderer = seriesRenderer is CartesianSeriesRenderer
          ? SeriesHelper.getSeriesRendererDetails(seriesRenderer).selectionBehaviorRenderer
          : _selectionBehaviorRenderer;
      _selectionBehaviorRenderer!._selectionDetails.selectionRenderer?.selectDataPoints(pointIndex, seriesIndex);
    }
  }

  List<int> getSelectedDataPoints(CartesianSeries<dynamic, dynamic> series) {
    List<ChartSegment> selectedItems = <ChartSegment>[];
    final dynamic seriesRenderer =
        _selectionBehaviorRenderer!._selectionDetails.stateProperties.chartSeries.visibleSeriesRenderers[0];
    SelectionBehaviorRenderer selectionRenderer;
    if (seriesRenderer is CartesianSeriesRenderer) {
      selectionRenderer = SeriesHelper.getSeriesRendererDetails(seriesRenderer).selectionBehaviorRenderer!;
    } else {
      selectionRenderer = seriesRenderer.selectionBehaviorRenderer;
    }

    final List<int> selectedPoints = <int>[];
    selectedItems = selectionRenderer._selectionDetails.selectionRenderer!.selectedSegments;
    for (int i = 0; i < selectedItems.length; i++) {
      selectedPoints.add(selectedItems[i].currentSegmentIndex!);
    }
    return selectedPoints;
  }
}

class SelectionBehaviorRenderer with ChartSelectionBehavior {
  SelectionBehaviorRenderer(SelectionBehavior selectionBehavior, dynamic chart, dynamic stateProperties) {
    _selectionDetails = ChartSelectionDetails(selectionBehavior, chart, stateProperties, this);
  }

  late ChartSelectionDetails _selectionDetails;


  @override
  Paint getSelectedItemFill(Paint paint, int seriesIndex, int pointIndex, List<ChartSegment> selectedSegments) => paint;

  @override
  Paint getUnselectedItemFill(Paint paint, int seriesIndex, int pointIndex, List<ChartSegment> unselectedSegments) =>
      paint;

  @override
  Paint getSelectedItemBorder(Paint paint, int seriesIndex, int pointIndex, List<ChartSegment> selectedSegments) =>
      paint;

  @override
  Paint getUnselectedItemBorder(Paint paint, int seriesIndex, int pointIndex, List<ChartSegment> unselectedSegments) =>
      paint;

  @override
  Color getCircularSelectedItemFill(Color color, int seriesIndex, int pointIndex, List<Region> selectedRegions) =>
      color;

  @override
  Color getCircularUnSelectedItemFill(Color color, int seriesIndex, int pointIndex, List<Region> unselectedRegions) =>
      color;

  @override
  Color getCircularSelectedItemBorder(Color color, int seriesIndex, int pointIndex, List<Region> selectedRegions) =>
      color;

  @override
  Color getCircularUnSelectedItemBorder(Color color, int seriesIndex, int pointIndex, List<Region> unselectedRegions) =>
      color;

  @override
  void onDoubleTap(double xPos, double yPos) =>
      _selectionDetails.selectionRenderer?.performSelection(Offset(xPos, yPos));

  @override
  void onLongPress(double xPos, double yPos) =>
      _selectionDetails.selectionRenderer?.performSelection(Offset(xPos, yPos));

  @override
  void onTouchDown(double xPos, double yPos) =>
      _selectionDetails.selectionRenderer?.performSelection(Offset(xPos, yPos));
}

class ChartSelectionDetails {
  ChartSelectionDetails(this.selectionBehavior, this.chart, this.stateProperties, this.selectionBehaviorRenderer);

  final dynamic chart;

  final dynamic stateProperties;

  final dynamic selectionBehavior;

  SelectionRenderer? selectionRenderer;

  final SelectionBehaviorRenderer selectionBehaviorRenderer;

  void selectRange() {
    bool isSelect = false;
    final SeriesRendererDetails seriesRendererDetails = selectionRenderer!.seriesRendererDetails;
    final CartesianStateProperties stateProperties = selectionRenderer!.stateProperties;
    if (selectionBehavior.enable == true && selectionBehavior.selectionController != null) {
      selectionBehavior.selectionController.addListener(() {
        stateProperties.isRangeSelectionSlider = true;
        selectionRenderer!.selectedSegments.clear();
        selectionRenderer!.unselectedSegments?.clear();
        final dynamic start = selectionBehavior.selectionController.start;
        final dynamic end = selectionBehavior.selectionController.end;
        for (int i = 0; i < seriesRendererDetails.dataPoints.length; i++) {
          final num xValue = seriesRendererDetails.dataPoints[i].xValue;
          isSelect = start is DateTime
              ? (xValue >= start.millisecondsSinceEpoch && xValue <= end.millisecondsSinceEpoch)
              : (xValue >= start && xValue <= end);

          isSelect
              ? selectionRenderer!.selectedSegments.add(seriesRendererDetails.segments[i])
              : selectionRenderer!.unselectedSegments?.add(seriesRendererDetails.segments[i]);
        }
        selectionRenderer!.selectedSegmentsColors(selectionRenderer!.selectedSegments);
        selectionRenderer!.unselectedSegmentsColors(selectionRenderer!.unselectedSegments!);

        for (final CartesianSeriesRenderer seriesRenderer in stateProperties.chartSeries.visibleSeriesRenderers) {
          ValueNotifier<int>(SeriesHelper.getSeriesRendererDetails(seriesRenderer).repaintNotifier.value++);
        }
      });
    }
    if (stateProperties.renderingDetails.initialRender! == true) {
      stateProperties.isRangeSelectionSlider = false;
    }
    selectionRenderer!.stateProperties = stateProperties;
  }
}

class SelectionHelper {
  const SelectionHelper._();

  static ChartSelectionDetails getRenderingDetails(SelectionBehaviorRenderer renderer) {
    return renderer._selectionDetails;
  }

  static void setSelectionBehaviorRenderer(
      SelectionBehavior selectionBehavior, SelectionBehaviorRenderer selectionBehaviorRenderer,) {
    selectionBehavior._selectionBehaviorRenderer = selectionBehaviorRenderer;
  }
}
